package cn.trigram.math;


import static ch.obermuhlner.math.big.DefaultBigDecimalMath.currentMathContext;

import java.math.BigDecimal;

public class RootNewtonItrMethod<T> {

  private final NewtonItrMethod newtonItrMethod;

  private final Number error;

  private final Number x0;

  public RootNewtonItrMethod(int root, Number eval) {

    if (eval instanceof Double || eval instanceof Float || eval instanceof Integer) {
      Double evalTemp = (Double) eval;
      newtonItrMethod = new DoubleNewtonItrMethod() {

        @Override
        public Double func(Double x) {

          Double t = x;
          for (int i = 1; i < root; i++) {
            t = t * x;
          }
          return t - evalTemp;
        }
      };
      this.x0         = newtonItrMethod.differenceQuotient(evalTemp, evalTemp / root);
      this.error      = 0.000000000000001d;
    } else if (eval instanceof BigDecimal) {

      BigDecimal evalTemp = ((BigDecimal) eval).multiply(BigDecimal.ONE, currentMathContext());
      newtonItrMethod = new BigDecimalNewtonItrMethod() {

        @Override
        public BigDecimal func(BigDecimal x) {

          BigDecimal t = x;
          for (int i = 1; i < root; i++) {
            t = t.multiply(x, currentMathContext());
          }
          return t.subtract(evalTemp, currentMathContext());
        }
      };
      this.x0         = newtonItrMethod.differenceQuotient(
          evalTemp, evalTemp.divide(new BigDecimal(root), currentMathContext()));
      error           = new BigDecimal("0.000000000000000000001", currentMathContext());
    } else {
      throw new UnsupportedOperationException();
    }
  }

  public T calcRoot() {

    return (T) newtonItrMethod.calc(x0, error);
  }

}
